Red Chainer の MNIST を試す
Red Chainer の MNIST を試す
Usage を試す
適当なアプリに Gemfile に入れて bundle install して bundle exec ruby examples/mnist/mnist.rb を実行したみた。
けど ruby: No such file or directory -- examples/mnist/mnist.rb (LoadError) になったよ ^^;
pathをちゃんと指定したら動いたよ。んーこんなんで良いんかな。
bundle exec ruby vendor/bundle/gems/red-chainer-0.4.1/examples/mnist/mnist.rb
結構時間かかりそう。今は約4分経過でプログレスバーに5%って表示されてるから、計80分くらいか?
2時間くらい掛かった。PCの自動スリープのせい?途中、一時的に設定いじって自動スリープを解除した。
code:sh
$ bundle exec ruby vendor/bundle/gems/red-chainer-0.4.1/examples/mnist/mnist.rb
GPU: -1
# unit: 1000
# Minibatch-size: 100
# epoch: 20
epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.194857 0.102614 0.940783 0.9679 318.217
2 0.0726359 0.0722306 0.977166 0.9782 640.927
3 0.048264 0.0876303 0.984798 0.9728 981.127
4 0.0372622 0.0662951 0.987898 0.9814 1344.15
5 0.0274703 0.0828177 0.990999 0.9778 2993.44
6 0.0235253 0.0814677 0.992165 0.98 3350.96
7 0.0196561 0.088254 0.993381 0.978 4417.14
8 0.0169085 0.0739303 0.994082 0.9827 4744.58
9 0.0183488 0.0721789 0.994182 0.9812 5100.57
10 0.0147982 0.0982596 0.995515 0.977 7954.95
11 0.0148762 0.108788 0.995415 0.9778 8296.37
12 0.00996762 0.0969031 0.997249 0.9812 8644.54
13 0.0144414 0.102498 0.995448 0.9804 8977.3
14 0.0115847 0.107862 0.996782 0.9794 9305.51
15 0.013216 0.101341 0.996232 0.9804 9630.1
16 0.00772914 0.0880115 0.997699 0.9838 9958.25
17 0.00524752 0.0863603 0.998399 0.9846 10287.9
18 0.0142592 0.116583 0.995949 0.9806 10620.4
19 0.00997321 0.101505 0.996882 0.9827 10953.6
20 0.0105345 0.103862 0.997132 0.9823 11286.2
これ結局なにやっとるん?
コード読んでみる
code:examples/mnist/mnist.rb
require 'chainer'
require 'fileutils'
require 'optparse'
require 'tmpdir'
class MLP < Chainer::Chain
L = Chainer::Links::Connection::Linear
R = Chainer::Functions::Activation::Relu
def initialize(n_units, n_out)
super()
init_scope do
@l1 = L.new(nil, out_size: n_units)
@l2 = L.new(nil, out_size: n_units)
@l3 = L.new(nil, out_size: n_out)
end
end
def call(x)
h1 = R.relu(@l1.(x))
h2 = R.relu(@l2.(h1))
@l3.(h2)
end
end
Chainer::Chain クラスを継承して MLP クラスを作っている
順伝播型(英語版)ニューラルネットワークの一分類である。MLPは少なくとも3つのノードの層からなる。入力ノードを除けば、個々のノードは非線形活性化関数を使用するニューロンである。MLPは学習のために誤差逆伝播法(バックプロパゲーション)と呼ばれる教師あり学習手法を利用する
ふーーん
3つのノードの層 が @l1, @l2, @l3 だな。 Chainer::Links::Connection::Linear のインスタンスを作ってる
Chainer::Links::Connection::Linear は何?Chainerに対する理解を深める必要がありそう。
n_units , n_outs は Linear への引数になってる
call メソッドは多分、Chainer::Chain クラスのメソッドを上書きしてるんじゃないかな。
以下のコードに call の記述がないから、どこかで呼ばれる仕組みになってるんだと推測してる
違うか。 Proc#call か?
Rubyの理解が全然足りない
Chainer::Functions::Activation::Relu クラスのクラスメソッド relu を使っている
code:lib/chainer/functions/activation/relu.rb
class Relu < FunctionNode
# Rectified Linear Unit function.
#
# $$
# f(x)=\\max(0, x).
# $$
#
# @return Chainer::Variable Output variable. A $(s_1, s_2, ..., s_N)$-shaped float array. # @example
# > x = Numo::SFloat-1, 0], 2, -3, [-2, 1 # > (x < 0).any?
# => true
# > F = Chainer::Functions::Activation::Relu
# > y = F.relu(x)
# > (y.data < 0).any?
# => false
# > y.shape
#
def self.relu(x)
y
end
def forward(x)
[Utils::Array.force_array(x0.class.maximum(x0, 0))] end
def backward(indexes, gy)
y = get_retained_outputs.first
ReLUGrad2.new(y).apply([gy0]) end
end
ふむ。
Relu クラスは FunctionNode クラスを継承している
apply は FunctionMode クラスのインスタンスメソッド。
Reluには forward メソッドと backward メソッドが実装されている
code:examples/mnist/mnist.rb
args = {
batchsize: 100,
frequency: -1,
epoch: 20,
resume: nil,
unit: 1000,
out: 'result'
}
opt = OptionParser.new
opt.on('-b', '--batchsize VALUE', "Number of images in each mini-batch (default: #{args:batchsize})") { |v| args:batchsize = v.to_i } opt.on('-e', '--epoch VALUE', "Number of sweeps over the dataset to train (default: #{args:epoch})") { |v| args:epoch = v.to_i } opt.on('-g', '--gpu VALUE', "GPU ID (negative value indicates CPU) (default: #{args:gpu})") { |v| args:gpu = v.to_i } opt.on('-o', '--out VALUE', "Directory to output the result (default: #{args:out})") { |v| args:out = v } opt.on('-r', '--resume VALUE', "Resume the training from snapshot") { |v| args:resume = v } opt.on('-u', '--unit VALUE', "Number of units (default: #{args:unit})") { |v| args:unit = v.to_i } opt.parse!(ARGV)
puts
設定値 args
コマンドライン引数で上書きできるようになってる。
require 'optparse' で OptionParser クラスが使えるようになってる
putsはコマンドラインに設定を表示してるだけ
code:examples/mnist/mnist.rb
device = Chainer::Device.create(args:gpu) Chainer::Device.change_default(device)
GPUを使う場合はdeviceを切り替える
code:examples/mnist/mnist.rb
lossfun = -> (x, t) { Chainer::Functions::Loss::SoftmaxCrossEntropy.new(ignore_label: nil).(x, t) }
model = Chainer::Links::Model::Classifier.new(MLP.new(args:unit, 10), lossfun) 損失関数を定義してる
分類器(?) Classifier としてモデルを定義している
1000 n_units, 10 n_out のMLP(多層パーセプトロン)と損失関数を与えている n_units = 中間層のノード数だよ〜
n_out = 出力層のノード数だよ〜
今回は 0 ~ 9 の10種類の数字に分類する分類問題だから n_out=10 なんだね
code:examples/mnist/mnist.rb
optimizer = Chainer::Optimizers::Adam.new
optimizer.setup(model)
最適化関数?的なものを、さっき定義したモデルを与えて定義している。
code:examples/mnist/mnist.rb
train, test = Chainer::Datasets::MNIST.get_mnist
train_iter = Chainer::Iterators::SerialIterator.new(train, args:batchsize) test_iter = Chainer::Iterators::SerialIterator.new(test, args:batchsize, repeat: false, shuffle: false) MNISTのデータセットを取得して、学習用データと検証用データに分ける それぞれの iterator を変数に入れてる
code:examples/mnist/mnist.rb
updater = Chainer::Training::StandardUpdater.new(train_iter, optimizer, device: device)
trainer = Chainer::Training::Trainer.new(updater, stop_trigger: [args:epoch, 'epoch'], out: args:out) 学習用 iterator と最適化関数?を与えて updater を定義している
その updater を使って trainer を定義している
code:examples/mnist/mnist.rb
trainer.extend(Chainer::Training::Extensions::Evaluator.new(test_iter, model, device: args:gpu)) 検証用 iterator とモデルを使って Evaluator を作り、trainer を拡張している
code:examples/mnist/mnist.rb
# Take a snapshot for each specified epoch
trainer.extend(Chainer::Training::Extensions::Snapshot.new, trigger: frequency, 'epoch', priority: -100) trainer.extend(Chainer::Training::Extensions::LogReport.new)
trainer.extend(Chainer::Training::Extensions::ProgressBar.new)
スナップショットやログ表示のために trainer を拡張している
コマンドライン引数を与えない場合は fequency = args[:epoch] = 20 になる
20回スナップショットを記録する、ということ?
code:examples/mnist/mnist.rb
Chainer::Serializers::MarshalDeserializer.load_file(args:resume, trainer) end
コマンドライン引数を与えない場合は args[:resume] = nil なのでこの分岐には入らない
code:examples/mnist/mnist.rb
trainer.run
学習を実行するぜ!、ということかな
20回学習が行なわれて、その精度がログに表示される、という感じだ。
改めて結果を見ると、徐々に精度が上がっているのがわかる